package org.edu.modules.act.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.CustomProperty;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.*;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ExecutionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLTextExtractor;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.edu.modules.act.common.result.PaginationBuilder;
import org.edu.modules.act.entity.*;
import org.edu.modules.act.mapper.*;
import org.edu.modules.act.provide.ProcessDiagramGeneratorWrapper;
import org.edu.modules.act.service.ProcessService;
import org.edu.modules.act.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Timestamp;
import java.util.*;

import static java.util.Collections.emptyList;

/**
 * 流程信息的业务逻辑实现层
 *
 * @版权所有 福州土著人信息科技有限公司
 */
@Service
@Transactional
public class ProcessServiceImpl implements ProcessService {

    private static final String DESCRIPTION = "description";
    private static final String SUSPENDED = "suspended";
    private static final String TENANTID = "tenantId";

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService turenTaskService;

    @Autowired
    private org.activiti.engine.TaskService taskService;

    @Autowired
    protected ProcessEngineConfiguration processEngineConfiguration;

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private ProcessDiagramGeneratorWrapper processDiagramGeneratorWrapper;

    @Resource
    private ActReWordModelMapper actReWordModelMapper;

    @Resource
    private ActRuInstanceFileMapper actRuInstanceFileMapper;

    @Resource
    private ActReModelMapper actReModelMapper;

    @Resource
    private IVeBaseTeacherMapper iVeBaseTeacherMapper;

//	@Autowired
//	private ActFileTemplateMapper actFileTemplateMapper;

    /**
     * 查询流程定义分页
     */
    @Override
    public Map<String, Object> queryProcessDefinition(Integer currentPage, Integer pageSize, String processDefinitionName) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        if (StringUtils.isNotBlank(processDefinitionName)) {
            processDefinitionQuery.processDefinitionNameLike("%" + processDefinitionName + "%");
        }
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage(pageSize * (currentPage - 1), pageSize);
        List<LinkedHashMap<String, Object>> resultList = new ArrayList<>();
        for (int i = 0; i < processDefinitionList.size(); i++) {
            LinkedHashMap<String, Object> processDefinitionMap = new LinkedHashMap<>();
            processDefinitionMap.put("id", processDefinitionList.get(i).getId());
            processDefinitionMap.put("category", processDefinitionList.get(i).getCategory());
            processDefinitionMap.put(DESCRIPTION, processDefinitionList.get(i).getDescription());
            processDefinitionMap.put("diagramResourceName", processDefinitionList.get(i).getDiagramResourceName());
            processDefinitionMap.put("deploymentId", processDefinitionList.get(i).getDeploymentId());
            processDefinitionMap.put("deployment",repositoryService.createDeploymentQuery().deploymentId(processDefinitionList.get(i).getDeploymentId()).singleResult().getCategory());
            processDefinitionMap.put("engineVersion", processDefinitionList.get(i).getEngineVersion());
            processDefinitionMap.put("key", processDefinitionList.get(i).getKey());
            processDefinitionMap.put("name", processDefinitionList.get(i).getName());
            processDefinitionMap.put("resourceName", processDefinitionList.get(i).getResourceName());
            processDefinitionMap.put(SUSPENDED, processDefinitionList.get(i).isSuspended());
            processDefinitionMap.put(TENANTID, processDefinitionList.get(i).getTenantId());
            processDefinitionMap.put("version", processDefinitionList.get(i).getVersion());
            System.out.println("deploymentId="+processDefinitionList.get(i).getDeploymentId());

            //获取模板的Url等数据
            QueryWrapper<ActReWordModel> wrapper = new QueryWrapper();
            //条件为表中MODEL_ID_等于参数modelId
            wrapper.eq("DEPLOYMENT_ID",processDefinitionList.get(i).getDeploymentId());
            //获取该实例的word模板
            List<ActReWordModel> wordList = actReWordModelMapper.selectList(wrapper);
            if(wordList!=null && wordList.size()>0) {
                String h5 = wordList.get(0).getWordH5();
                System.out.println("H5旧的东西" + h5);
                //删减冗余的标签
                h5 = h5.replace("<span style=\"font-family:'等线';font-size:16.0pt;\">", "").replace("</span>", "");
                //替换word的标记符号为 ""
                for (int j = 1; j < 11; j++) {
                    h5 = h5.replace("{{auditOpinion" + j + "}}", "");
                    h5 = h5.replace("{{auditName" + j + "}}", "");
                    h5 = h5.replace("{{auditTime" + j + "}}", "");
                }
                System.out.println("H5新的东西" + h5);
                wordList.get(0).setWordH5(h5);
                processDefinitionMap.put("wordFile", wordList.get(0));
            }else{
                System.out.println("DEPLOYMENT_ID : " + processDefinitionList.get(i).getDeploymentId() + " 没有找到模板 act_re_word_model");
            }

            resultList.add(processDefinitionMap);
        }

//		org.springframework.security.core.Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//		String roleData = sysRoleServiceClient.queryRoleData("processdefinition", authentication.getName());
//		String[] roleDataArray = roleData == null ? null : roleData.split(",");
//		if (roleDataArray != null && roleDataArray.length > 0) {// 处理数据权限
//			return PaginationBuilder.buildResult(CollectionUtils.convertFilterList(resultList, roleDataArray), processDefinitionQuery.count(), currentPage,
//					pageSize);
//		} else {
//			return PaginationBuilder.buildResult(resultList, processDefinitionQuery.count(), currentPage, pageSize);
//		}
        return PaginationBuilder.buildResult(resultList, processDefinitionQuery.count(), currentPage, pageSize);
    }

    /**
     * 查询流程实例分页
     */
    @Override
    public Map<String, Object> queryProcessInstance(Integer currentPage, Integer pageSize, String processInstanceName) {
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        if (StringUtils.isNotBlank(processInstanceName)) {
            processInstanceQuery.processInstanceNameLike("%" + processInstanceName + "%");
        }
        List<ProcessInstance> processInstanceList = processInstanceQuery.includeProcessVariables().listPage(pageSize * (currentPage - 1), pageSize);
        List<LinkedHashMap<String, Object>> resultList = new ArrayList<>();
        for (int i = 0; i < processInstanceList.size(); i++) {
            LinkedHashMap<String, Object> processInstanceMap = new LinkedHashMap<>();
            processInstanceMap.put("id", processInstanceList.get(i).getId());
            processInstanceMap.put("activityId", processInstanceList.get(i).getActivityId());
            processInstanceMap.put("businessKey", processInstanceList.get(i).getBusinessKey());
            processInstanceMap.put(DESCRIPTION, processInstanceList.get(i).getDescription());
            processInstanceMap.put("deploymentId", processInstanceList.get(i).getDeploymentId());
            processInstanceMap.put("ended", processInstanceList.get(i).isEnded());
            processInstanceMap.put("name", processInstanceList.get(i).getName());
            processInstanceMap.put("processInstanceId", processInstanceList.get(i).getProcessInstanceId());
            processInstanceMap.put("processDefinitionId", processInstanceList.get(i).getProcessDefinitionId());
            processInstanceMap.put("processDefinitionKey", processInstanceList.get(i).getProcessDefinitionKey());
            processInstanceMap.put("processDefinitionName", processInstanceList.get(i).getProcessDefinitionName());
            processInstanceMap.put("processDefinitionVersion", processInstanceList.get(i).getProcessDefinitionVersion());
            processInstanceMap.put("processVariables", processInstanceList.get(i).getProcessVariables());
            processInstanceMap.put("startUserId", processInstanceList.get(i).getStartUserId());
            processInstanceMap.put("startTime", processInstanceList.get(i).getStartTime());
            processInstanceMap.put(SUSPENDED, processInstanceList.get(i).isSuspended());
            processInstanceMap.put(TENANTID, processInstanceList.get(i).getTenantId());

            //根据部署ID去查找模型
            QueryWrapper<ActReModelEntity> wrapper=new QueryWrapper<>();
            wrapper.eq("DEPLOYMENT_ID_",processInstanceList.get(i).getDeploymentId());
            List<ActReModelEntity> list = actReModelMapper.selectList(wrapper);
            processInstanceMap.put("modelInfo",list.get(0));

            //查询我的任务时，获取用户信息（taecher表）
            QueryWrapper<VeBaseTeacherEntity> wrapper2 = new QueryWrapper<>();
            wrapper2.eq("userId",processInstanceList.get(i).getStartUserId());
            List<VeBaseTeacherEntity> list2 = iVeBaseTeacherMapper.selectList(wrapper2);
            processInstanceMap.put("startUserInfo", list2.get(0));

            resultList.add(processInstanceMap);
        }
//		org.springframework.security.core.Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//		String roleData = sysRoleServiceClient.queryRoleData("processinstance", authentication.getName());
//		String[] roleDataArray = roleData == null ? null : roleData.split(",");
//		if (roleDataArray != null && roleDataArray.length > 0) {// 处理数据权限
//			return PaginationBuilder.buildResult(CollectionUtils.convertFilterList(resultList, roleDataArray), processInstanceQuery.count(), currentPage,
//					pageSize);
//		} else {
//			return PaginationBuilder.buildResult(resultList, processInstanceQuery.count(), currentPage, pageSize);
//		}
        return PaginationBuilder.buildResult(resultList, processInstanceQuery.count(), currentPage, pageSize);
    }

    /**
     * 查询流程实例执行路径分页
     */
    @Override
    public Map<String, Object> queryExecution(Integer currentPage, Integer pageSize, String processInstanceId) {
        ExecutionQuery executionQuery = runtimeService.createExecutionQuery();
        if (StringUtils.isNotBlank(processInstanceId)) {
            executionQuery.processInstanceId(processInstanceId);
        }
        List<Execution> executionList = executionQuery.listPage(pageSize * (currentPage - 1), pageSize);
        List<LinkedHashMap<String, Object>> resultList = new ArrayList<>();
        for (int i = 0; i < executionList.size(); i++) {
            LinkedHashMap<String, Object> executionMap = new LinkedHashMap<>();
            executionMap.put("id", executionList.get(i).getId());
            executionMap.put("activityId", executionList.get(i).getActivityId());
            executionMap.put(DESCRIPTION, executionList.get(i).getDescription());
            executionMap.put("name", executionList.get(i).getName());
            executionMap.put("parentId", executionList.get(i).getParentId());
            executionMap.put("parentProcessInstanceId", executionList.get(i).getParentProcessInstanceId());
            executionMap.put("processInstanceId", executionList.get(i).getProcessInstanceId());
            executionMap.put("rootProcessInstanceId", executionList.get(i).getRootProcessInstanceId());
            executionMap.put("superExecutionId", executionList.get(i).getSuperExecutionId());
            executionMap.put(TENANTID, executionList.get(i).getTenantId());
            executionMap.put("ended", executionList.get(i).isEnded());
            executionMap.put(SUSPENDED, executionList.get(i).isSuspended());
            resultList.add(executionMap);
        }

//		org.springframework.security.core.Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//		String roleData = sysRoleServiceClient.queryRoleData("execution", authentication.getName());
//		String[] roleDataArray = roleData == null ? null : roleData.split(",");
//		if (roleDataArray != null && roleDataArray.length > 0) {// 处理数据权限
//			return PaginationBuilder.buildResult(CollectionUtils.convertFilterList(resultList, roleDataArray), executionQuery.count(), currentPage, pageSize);
//		} else {
//			return PaginationBuilder.buildResult(resultList, executionQuery.count(), currentPage, pageSize);
//		}
        return PaginationBuilder.buildResult(resultList, executionQuery.count(), currentPage, pageSize);
    }

    /**
     * 获取流程定义XML
     */
    @Override
    public String getProcessResource(String deploymentId) {
        ProcessDefinition processDefinition = checkProcessDefinition(deploymentId);
        String bpmnResourceName = processDefinition.getResourceName();
        final InputStream deploymentInputStream = repositoryService.getResourceAsStream(deploymentId, bpmnResourceName);
        byte[] bytes = IoUtil.readInputStream(deploymentInputStream, "input stream");
        return new String(bytes);
    }

    /**
     * 获取高亮跟踪流程图
     */
    @Override
    public String getHighLightedProcessDiagram(String processInstanceId) {
        //securityUtil.logInAs("admin");
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            throw new ActivitiException("流程已结束");
        }
        org.activiti.api.process.model.ProcessInstance processInstance = processRuntime.processInstance(processInstanceId);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        return new String(processDiagramGeneratorWrapper.generateDiagram(bpmnModel,
                processRuntime.processInstanceMeta(processInstance.getId()).getActiveActivitiesIds(), emptyList()), StandardCharsets.UTF_8);
    }

    /**
     * 获取流程定义图片
     */
    @Override
    public InputStream getProcessImage(String deploymentId) {
        ProcessDefinition processDefinition = checkProcessDefinition(deploymentId);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        InputStream inputStream = null;
        if (bpmnModel != null && bpmnModel.getLocationMap().size() > 0) {
            ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
            inputStream = processDiagramGenerator.generateDiagram(bpmnModel, emptyList(), emptyList(), "宋体", "宋体", "宋体", true);
        }
        return inputStream;
    }

    /**
     * 校验流程定义是否存在
     */
    private ProcessDefinition checkProcessDefinition(String deploymentId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if (processDefinition == null) {
            throw new ActivitiObjectNotFoundException("找不到此流程定义", ProcessDefinition.class);
        }
        return processDefinition;
    }

    /**
     * 持久化流程
     */
    @Override
    public void persistProcess(String modelId, String jsonXml, String svgXml) {
        System.out.println("modelId" + modelId);
        System.out.println("jsonXml" + jsonXml);
        System.out.println("svgXml" + svgXml);
        Model model = repositoryService.getModel(modelId);
        repositoryService.addModelEditorSource(model.getId(), jsonXml.getBytes(StandardCharsets.UTF_8));
        repositoryService.addModelEditorSourceExtra(model.getId(), svgXml.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 发起流程
     */
    @Override
    public void startProcessInstance(Leave leave, String starterUser,String userName,String fileId) {
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(leave.getProcessDefinitionId());
        if (processDefinition.isSuspended()) {
            throw new ActivitiIllegalArgumentException("流程定义已挂起，不能发起");
        }
        Authentication.setAuthenticatedUserId(starterUser);
        Map<String, Object> startVariables = new HashMap<>();
        System.out.println("测试:"+processDefinition.getId());
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        List<org.activiti.bpmn.model.Process> processList = bpmnModel.getProcesses();


//		startVariables.put("startTime", leave.getStartTime());
//		startVariables.put("endTime", leave.getEndTime());
//		startVariables.put(DESCRIPTION, "备注");
//		String[] customForm = leave.getCustomForm();// 自定义字段
//		for (int i = 0; i < customForm.length; i++) {
//			startVariables.put("自定义字段" + i + 1, customForm[i]);
//		}

        ProcessInstance processInstance = runtimeService.startProcessInstanceById(leave.getProcessDefinitionId(), startVariables);

        runtimeService.setProcessInstanceName(processInstance.getId(), userName + "发起的流程");

        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
        if (task != null) {
//			if (StringUtils.isBlank(leave.getCandidate())) {
//				throw new ActivitiIllegalArgumentException("流程有待审批，请正确选择审批人员");
//			}
            System.out.println(task.getName()+"审批");
            String approvedBy =processList.get(0).getFlowElement(task.getTaskDefinitionKey()).getAttributes().get("assignee").get(0).getValue();
            System.out.println(approvedBy);
            startVariables.put("candidate",approvedBy );
            turenTaskService.updateTask(task.getId(), approvedBy);
            turenTaskService.claimTask(task.getId(), approvedBy);//直接签收 流到待操作
        }

        //发起流程时候将流程实例和文件表关联
        ActRuInstanceFileEntity actRuInstanceFileEntity=new ActRuInstanceFileEntity();
        if(fileId!=null){
            //类型转换
            Long id=Long.parseLong(fileId);
            actRuInstanceFileEntity.setId(id);
            //获取文件实例的ID；
            actRuInstanceFileEntity.setProcInstId(processInstance.getId());
            //修改文件表的实例ID
            actRuInstanceFileMapper.updateById(actRuInstanceFileEntity);
        }
    }

    /**
     * 挂起流程定义
     */
    @Override
    public void suspendProcessDefinition(String processDefinitionId) {
        if (repositoryService.isProcessDefinitionSuspended(processDefinitionId)) {
            throw new ActivitiException("ID为'" + processDefinitionId + " '的流程定义已挂起");
        }
        repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null); // true是指包含流程实例
    }

    /**
     * 激活流程定义
     */
    @Override
    public void activateProcessDefinition(String processDefinitionId) {
        if (!repositoryService.isProcessDefinitionSuspended(processDefinitionId)) {
            throw new ActivitiException("ID为'" + processDefinitionId + " '的流程定义已激活");
        }
        repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);// true是指包含流程实例
    }

    /**
     * 挂起流程实例
     */
    @Override
    public void suspendProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = checkProcessInstance(processInstanceId);
        if (processInstance.isSuspended()) {
            throw new ActivitiException("ID为'" + processInstance.getId() + " '的流程实例已挂起");
        }
        runtimeService.suspendProcessInstanceById(processInstance.getId());
    }

    /**
     * 激活流程实例
     */
    @Override
    public void activateProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = checkProcessInstance(processInstanceId);
        if (!processInstance.isSuspended()) {
            throw new ActivitiException("ID为'" + processInstance.getId() + " '的流程实例已激活");
        }
        runtimeService.activateProcessInstanceById(processInstance.getId());
    }

    /**
     * 删除或终止流程实例
     */
    @Override
    public void deleteProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = checkProcessInstance(processInstanceId);
        runtimeService.deleteProcessInstance(processInstance.getId(), "删除ID为'" + processInstanceId + " '的流程实例");
    }

    /**
     * 校验流程实例是否存在
     */
    private ProcessInstance checkProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            throw new ActivitiObjectNotFoundException("找不到ID为'" + processInstanceId + "'的流程实例");
        }
        return processInstance;
    }



    /**
     * 上传文件到与jar包同级的外部目录
     *
     * @param fileName 已修改成时间戳的文件名称
     * @return
     * @throws IOException
     */
    private String generatePath(String fileName) throws IOException {
        File rootDirectoryPath = new File(ResourceUtils.getURL("classpath:").getPath());
        if (!rootDirectoryPath.exists()) {
            rootDirectoryPath = new File("");
        }
        File uploadPath = new File(rootDirectoryPath.getAbsolutePath(), "static/activiti/template");
        if (!uploadPath.exists()) {
            uploadPath.mkdirs();
        }
        return rootDirectoryPath.getAbsolutePath() + "/static/activiti/template/" + fileName;
    }

}
